using System;
using System.Collections.Generic;
using Microsoft.Practices.ObjectBuilder;
using ObjectContainer.Configuration;

namespace ObjectContainer
{

    public class Container : IDisposable
    {
        private IBuilder<BuilderStage> _builder;
        private Locator _locator;
        private LifetimeContainer _container;

        public Container(): this(null){}

        /// <summary>
        /// Creates a new instance based on the specified configuration.
        /// </summary>
        /// <param name="xml">A string with the XML configuration.</param>
        public Container(string xml)
        {
            _builder = CreateBuilder(xml == null ? null : new BuilderXmlConfig(xml));
            _locator = new Locator();
            _container = new LifetimeContainer();
            _locator.Add(typeof(ILifetimeContainer), _container);
        }

        private IBuilder<BuilderStage> CreateBuilder(IBuilderConfigurator<BuilderStage> configurator)
        {
            IBuilder<BuilderStage> result = new BuilderBase<BuilderStage>();

            result.Strategies.AddNew<TypeMappingStrategy>(BuilderStage.PreCreation);
            result.Strategies.AddNew<SingletonStrategy>(BuilderStage.PreCreation);
            result.Strategies.AddNew<ConstructorReflectionStrategy>(BuilderStage.PreCreation);
            result.Strategies.AddNew<PropertyReflectionStrategy>(BuilderStage.PreCreation);
            result.Strategies.AddNew<MethodReflectionStrategy>(BuilderStage.PreCreation);
            result.Strategies.AddNew<CreationStrategy>(BuilderStage.Creation);
            result.Strategies.AddNew<PropertySetterStrategy>(BuilderStage.Initialization);
            result.Strategies.AddNew<MethodExecutionStrategy>(BuilderStage.Initialization);
            result.Strategies.AddNew<BuilderAwareStrategy>(BuilderStage.PostInitialization);

            result.Policies.SetDefault<ICreationPolicy>(new DefaultCreationPolicy());

            if (configurator != null)
                configurator.ApplyConfiguration(result);

            return result;
        }

        public TBuild Get<TBuild>()
        {
            return _builder.BuildUp<TBuild>(_locator, null, null);
        }

        public void RegisterSingleton<TBuild>()
        {
            _builder.Policies.Set<ISingletonPolicy>(new SingletonPolicy(true), typeof(TBuild), null);
        }

        public void RegisterTypeMapping<TRequested, TToBuild>()
        {
            _builder.Policies.Set<ITypeMappingPolicy>(new TypeMappingPolicy(typeof(TToBuild), null), typeof(TRequested), null);
        }

        public void Dispose()
        {
            List<object> items = new List<object>();
            items.AddRange(_container);

            foreach (object item in items)
                _builder.TearDown(_locator, item);

            _container.Dispose();
            _container = null;
            _locator = null;
            _builder = null;
        }
    }


}
